﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Xml.Linq;

using ProjectBase.Core;
using Constants = ProjectBase.Tools.Publishing.Handlers.Constants;

namespace ProjectBase.Tools.Publishing
{
    public class HandlerConfigurator
    {
        public virtual IEnumerable<IPublishingHandler> CreateHandlers(XElement settings)
        {
            Contract.Requires(settings.Is(), "Publishing HandlerConfigurator: Missing configuration settings, cannot create IPublishingHandler set");

            var list = new List<IPublishingHandler>();

            var handlers = settings.Descendants(Constants.ElementHandlers).FirstOrDefault();
            if (handlers.Is())
            {
                ActivateHandlers(handlers, list);
            }

            if (list.IsEmpty())
            {
                list.Add(new NullPublishingHandler());
            }

            return list;
        }

        public virtual IPublishingHandler CreateHandlersChain(IEnumerable<IPublishingHandler> handlers)
        {
            Contract.Requires(handlers.Is(), "Publishing HandlerConfigurator: null handlers set was provided");
            if (handlers.Count() <= 1)
            {
                return handlers.FirstOrDefault();
            }
            for (var i = 0; i < (handlers.Count() - 1); i++)
            {
                handlers.ElementAt(i).SetSuccessor(handlers.ElementAt(i + 1));
            }
            return handlers.First();
        }

        #region protected
        protected virtual void ActivateHandlers(XElement handlers, IList<IPublishingHandler> list)
        {
            Contract.Requires(handlers.Is());
            Contract.Requires(list.Is());

            foreach (var handlerElement in handlers.Elements(Constants.ElementHandler))
            {
                var handlerAttr = handlerElement.Attribute(Constants.AttributeType);
                if (handlerAttr.Is())
                {
                    ActivateHandler(list, handlerAttr, handlerElement);
                }
            }
        }
        protected virtual void ActivateHandler(IList<IPublishingHandler> list, XAttribute handlerAttribute, XElement handlerSettings)
        {
            Contract.Requires(list.Is(), "Publishing HandlerConfigurator: passed handlers 'list' cannot be null.");
            Contract.Requires(handlerAttribute.Is(), "Publishing HandlerConfigurator: passed 'handlerAttribute' cannot be null");

            var type = Type.GetType(handlerAttribute.Value);
            if (type.IsNull())
            {
                throw new ConfigurationErrorsException("Publishing HandlerConfigurator: handler type '{0}' does not exist."
                    .FormatWith(handlerAttribute.Value));
            }

            try
            {
                var handler = Activator.CreateInstance(type) as IPublishingHandler;
                handler.Priority = list.Count;
                handler.Init(handlerSettings);
                list.Add(handler);
            }
            catch (ConfigurationErrorsException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new ConfigurationErrorsException("Publishing HandlerConfigurator: handler type {0} could not be created. See inner exception."
                    .FormatWith(handlerAttribute.Value)
                    , ex);
            }
        }
        #endregion protected
    }
}
